package cn.ljy.mes.service.impl;

import cn.hutool.core.util.IdUtil;
import cn.ljy.authority.config.security.UserUtil;
import cn.ljy.authority.model.SysUser;
import cn.ljy.authority.service.impl.IBaseService;
import cn.ljy.common.exception.model.MyDataException;
import cn.ljy.common.exception.model.MyServiceException;
import cn.ljy.common.exception.model.MyWebException;
import cn.ljy.mes.dao.mapper.*;
import cn.ljy.mes.model.*;
import cn.ljy.mes.model.common.enums.ReservationAction;
import cn.ljy.mes.model.query.WmsMaterialInventoryDetailQuery;
import cn.ljy.mes.service.WmsMaterialInventoryDetailService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;

import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Service
//@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT,timeout=36000,rollbackFor= MyRollBackException.class)
@Validated//表示开启sprint的校检框架，会自动扫描方法里的@Valid（@Valid注解一般写在接口即可）
@Slf4j
public class IWmsMaterialInventoryDetailService extends IBaseService<WmsMaterialInventoryDetail, WmsMaterialInventoryDetailQuery> implements WmsMaterialInventoryDetailService {
    @Autowired
    private WmsMaterialInventoryMapper materialInventoryMapper;
    @Autowired
    private WmsMaterialInventoryDetailMapper materialInventoryDetailMapper;
    @Autowired
    private WmsMaterialBatchMapper materialBatchMapper;
    @Autowired
    private WmsWarehouseSeatMapper wmsWarehouseSeatMapper;
    @Autowired
    private WmsMaterialInventoryDetailRelocationLogMapper materialInventoryDetailRelocationLogMapper;
    @Autowired
    private WmsMaterialInventoryDetailLockLogMapper wmsMaterialInventoryDetailLockLogMapper;

    @Override
    protected void beforeSave(WmsMaterialInventoryDetail materialInventoryDetail) {
        //1.基本表单验证
        if(materialInventoryDetail.getMaterialInventory().getId()==null){
            throw new MyWebException("验证失败：必须选择所属库存");
        }
        if(materialInventoryDetail.getMaterialBatch().getId()==null){
            throw new MyWebException("验证失败：必须选择物料批次");
        }
        //2.判断物料库存是否锁定
        //获取物料库存
        WmsMaterialInventory materialInventory=materialInventoryMapper.findByIdNoRelation(materialInventoryDetail.getMaterialInventory().getId());
        if(materialInventory.getIsLock()){
            throw new MyServiceException("保存失败：该物料库存已经锁定，无法对其库存进行操作");
        }
        //3.判断物料数量是否超出库存上限
        long totalInventory=materialInventory.getTotalInventory()+materialInventoryDetail.getInventory();
        if(materialInventory.getInventoryLimit()<totalInventory){
            throw new MyWebException(String.format("验证失败：存入后物料库存为%d，超过该物料库存上限%d",totalInventory,materialInventory.getInventoryLimit()));
        }
        //4.验证目标库位
        if(materialInventoryDetail.getWarehouseSeat()==null||!StringUtils.hasLength(materialInventoryDetail.getWarehouseSeat().getId())){
            throw new MyWebException("操作失败：库位信息不能为空");
        }else{
            WmsWarehouseSeat warehouseSeat = wmsWarehouseSeatMapper.findById(materialInventoryDetail.getWarehouseSeat().getId());
            if(warehouseSeat==null){
                throw new MyWebException("操作失败：目标库位不存在");
            }else{
                //更新为目标库位信息
                materialInventoryDetail.setWarehouseSeat(warehouseSeat);
            }
        }
    }

    /**
     * 重新订单添加（入库）
     * 说明：
     * 1.如果该物料是第一次存入该库位，则直接增加一条新记录
     * 2.如果该物料批次已经在某库位上存在（物料和批次均相同），再次入库到该库位则让该库位数量=原物料批次数量+本次入库物料批次数量。
     * 3.
     * @param materialInventoryDetail 部分字段必须有值，详见文档
     */
    @Override
    public void save(WmsMaterialInventoryDetail materialInventoryDetail) {
        //先做验证
        beforeSave(materialInventoryDetail);
        long inventoryOrign=0;//获取库位详情中的原库存数量，用于移库日志的记录。默认没有记录
        //1.判断物料批次是否锁定
        WmsMaterialBatch materialBatchOrign = materialBatchMapper.findByIdNoRelation(materialInventoryDetail.getMaterialBatch());
        if(materialBatchOrign.getIsLock()){
            throw new MyServiceException("保存失败：该批次物料已经锁定，无法对其库存进行操作");
        }
        //2.判断获取该物料批次的入库数量是否超过预期
        long pendingQuantity=materialBatchOrign.getPurchaseQuantity()-materialBatchOrign.getStoredQuantity();//未入库数量
        if(materialInventoryDetail.getInventory()>pendingQuantity){
            throw new MyServiceException("保存失败：存入数量超过该物料批次未入库数量");
        }
        //3.判断原来的物料库存详情中，是否已经在同样的库位中存有相同的物料批次
        WmsMaterialInventoryDetailQuery inventoryDetailQuery=new WmsMaterialInventoryDetailQuery();
        inventoryDetailQuery.setWarehouseSeat(materialInventoryDetail.getWarehouseSeat());
        inventoryDetailQuery.setMaterialBatch(materialInventoryDetail.getMaterialBatch());
        List<WmsMaterialInventoryDetail> inventoryDetails=mapper.findAll(inventoryDetailQuery);
        boolean saveSuccess=true;//记录是否成功录入
        if(inventoryDetails.size()==1){//如果已经有重复记录
            //获取原来库位的相同物料批次信息
            WmsMaterialInventoryDetail inventoryDetailOrign=inventoryDetails.get(0);
            //4.判断该物料库存是否锁定
            if(inventoryDetailOrign.getIsLock()){
                throw new MyServiceException("保存失败：该物料的库位已经被锁定，无法执行库存操作，请选择其他库位");
            }
            //将库存数量累加
            inventoryOrign=inventoryDetailOrign.getInventory();
            inventoryDetailOrign.setInventory(inventoryDetailOrign.getInventory()+materialInventoryDetail.getInventory());
            mapper.update(inventoryDetailOrign);
        }else if(inventoryDetails.size()==0){//如果不存在
            mapper.save(materialInventoryDetail);
        }else{
            saveSuccess=false;
            throw new MyDataException("数据库的库存详情表中存在相同物料批次和库位的信息，请进行核查！");
        }
        if(saveSuccess){//如果订单详情添加成功
            //1.手动更新物料批次中的已入库数量信息
            materialBatchOrign.setStoredQuantity(materialBatchOrign.getStoredQuantity()+materialInventoryDetail.getInventory());
            materialBatchMapper.update(materialBatchOrign);
            //2.将物料入库记录存入日志中
            WmsMaterialInventoryDetailRelocationLog materialInventoryDetailRelocationLog=new WmsMaterialInventoryDetailRelocationLog();
            materialInventoryDetailRelocationLog.setMaterialBatch(materialBatchOrign);//设置物料批次
            materialInventoryDetailRelocationLog.setWarehouseSeatOfTarget(materialInventoryDetail.getWarehouseSeat());//设置转移的目标库位。因为是入库，不用设置源库位
            materialInventoryDetailRelocationLog.setTargetInventory(inventoryOrign);//原库位的库存
            materialInventoryDetailRelocationLog.setRelocationQuantity(materialInventoryDetail.getInventory());//转移的库存数量
            materialInventoryDetailRelocationLog.setReason(materialInventoryDetail.getRemark());//移库原因
            //设置操作用户信息
            SysUser loginUser = UserUtil.getLoginUser();//获取当前登录用户
            materialInventoryDetailRelocationLog.setExecutor(loginUser);
            //存储移库日志
            materialInventoryDetailRelocationLogMapper.save(materialInventoryDetailRelocationLog);
        }
    }

    @Override
    public void relocation(WmsMaterialInventoryDetail materialInventoryDetailOfSource, WmsMaterialInventoryDetail materialInventoryDetailOfTarget) {
        long sourceInventory=0;//获取源库位详情中的原库存数量，用于移库日志的记录。默认没有记录
        long targetInventory=0;//获取目标库位详情中的原库存数量，用于移库日志的记录。默认没有记录
        long relocationQuantity=0;//获取要转移的库存数量
        //1.先对表单信息进行验证
        if(!StringUtils.hasLength(materialInventoryDetailOfSource.getId())){
            throw new MyWebException("操作失败：源库位详情的标识符不能为空");
        }
        if(materialInventoryDetailOfTarget.getWarehouseSeat()==null||!StringUtils.hasLength(materialInventoryDetailOfTarget.getWarehouseSeat().getId())){
            throw new MyWebException("操作失败：目标库位信息不能为空");
        }else{
            //因为要存入移库日志，这里要一次查库区仓库名称出来。
            WmsWarehouseSeat warehouseSeatTarget = wmsWarehouseSeatMapper.findById(materialInventoryDetailOfTarget.getWarehouseSeat().getId());
            if(warehouseSeatTarget==null){
                throw new MyWebException("操作失败：目标库位不存在");
            }else{
                //更新为目标库位信息
                materialInventoryDetailOfTarget.setWarehouseSeat(warehouseSeatTarget);
            }
        }
        if(materialInventoryDetailOfTarget.getInventory()==null){
            throw new MyWebException("操作失败：移入目标库位的商品数量不能为空");
        }else{
            if(materialInventoryDetailOfTarget.getInventory()<=0){
                throw new MyWebException("操作失败：移入目标库位的商品数量必须大于0");
            }
        }
        if(materialInventoryDetailOfTarget.getRemark()!=null&&materialInventoryDetailOfTarget.getRemark().length()>255){
            throw new MyWebException("操作失败：移入目标库位的备注长度不能超过255个字符");
        }
        relocationQuantity=materialInventoryDetailOfTarget.getInventory();//获取要转移的库存数量
        //2.验证业务信息
        //获取源库位详情在系统中的数据
        materialInventoryDetailOfSource=mapper.findById(materialInventoryDetailOfSource.getId());
        if(materialInventoryDetailOfSource!=null){
            sourceInventory=materialInventoryDetailOfSource.getInventory();//获取源库位详情中的原库存数量
            if(materialInventoryDetailOfTarget.getWarehouseSeat().getId().equals(materialInventoryDetailOfSource.getWarehouseSeat().getId())){
                throw new MyWebException("操作失败：源库位和目标库位不能相同");
            }else if(materialInventoryDetailOfSource.getIsLock()){
                throw new MyServiceException("操作失败：源库位的该商品已经锁定，无法进行移库操作");
            }else if(materialInventoryDetailOfSource.getMaterialInventory().getIsLock()){
                throw new MyServiceException("操作失败：该商品的库存已经锁定，无法进行移库操作");
            }else if(materialInventoryDetailOfSource.getMaterialBatch().getIsLock()){
                throw new MyServiceException("操作失败：该批次的商品经锁定，无法进行移库操作");
            }else if((materialInventoryDetailOfSource.getInventory()-materialInventoryDetailOfSource.getLockQuantity())<materialInventoryDetailOfTarget.getInventory()){
                //后续要补充锁定数量的差额
                throw new MyServiceException("操作失败：移入目标库位的商品数量超出源库位的商品数量(库存数量-锁定数量)");
            }
        }else{
            throw new MyServiceException("操作失败：源库位信息不存在");
        }
        //3.存入新库位信息
        materialInventoryDetailOfTarget.setMaterialInventory(materialInventoryDetailOfSource.getMaterialInventory());//设置物料库存信息
        materialInventoryDetailOfTarget.setMaterialBatch(materialInventoryDetailOfSource.getMaterialBatch());//设置物料批次信息
        //3.1 对目标库位进行保存处理
        //如果目标库位已经存储有相关批次的物料信息，则更新原库位信息；
        WmsMaterialInventoryDetailQuery query=new WmsMaterialInventoryDetailQuery();
        query.setMaterialInventory(materialInventoryDetailOfTarget.getMaterialInventory());
        query.setMaterialBatch(materialInventoryDetailOfTarget.getMaterialBatch());
        query.setWarehouseSeat(materialInventoryDetailOfTarget.getWarehouseSeat());
        List<WmsMaterialInventoryDetail> materialInventoryDetailList = mapper.findAll(query);
        if(materialInventoryDetailList.size()==1){
            //如果目标库位已经存储相关批次的物料信息，则让该库位的商品数量相加
            WmsMaterialInventoryDetail materialInventoryDetailOfTargetNew=materialInventoryDetailList.get(0);
            targetInventory=materialInventoryDetailOfTargetNew.getInventory();//获取目标库位的原库存数量
            //让库位中的商品数量相加
            materialInventoryDetailOfTargetNew.setInventory(materialInventoryDetailOfTargetNew.getInventory()+materialInventoryDetailOfTarget.getInventory());
            //更新数据库
            mapper.update(materialInventoryDetailOfTargetNew);
        }else if(materialInventoryDetailList.size()==0){
            //如果目标库位还未存储相关批次的物料信息，则添加新的信息。
            materialInventoryDetailOfTarget.setIsLock(false);//设定锁定状态为false
            mapper.save(materialInventoryDetailOfTarget);
        }else{
            throw new MyServiceException(String.format("操作失败：数据库异常，库位（%s-%s-%s）上存在相同批次的物料信息(物料名：%s，批次：%s)"
                    ,materialInventoryDetailOfTarget.getWarehouseSeat().getWarehouseArea().getWarehouse().getName()
                    ,materialInventoryDetailOfTarget.getWarehouseSeat().getWarehouseArea().getName()
                    ,materialInventoryDetailOfTarget.getWarehouseSeat().getName()
                    ,materialInventoryDetailOfTarget.getMaterialBatch().getMaterial().getName()
                    ,materialInventoryDetailOfTarget.getMaterialBatch().getBatchNumber()));
        }
        //3.2 对源库位信息进行处理
        //如果是将源库位的指定商品全部移入目标库位，则要删除源库位的信息（物料业务中貌似要保存，但是在本系统因为要做太多测试，因此暂时选择移除）
        if(materialInventoryDetailOfSource.getInventory()==materialInventoryDetailOfTarget.getInventory()){
            if(materialInventoryDetailOfSource.getLockQuantity()==0){//如果锁定数量为0，则可以删除
                //移除源库位详情
                mapper.deleteById(materialInventoryDetailOfSource.getId());
            }
        }else{
            //将源库位的物料数量减少：原商品数量-移库的数量
            materialInventoryDetailOfSource.setInventory(materialInventoryDetailOfSource.getInventory()-materialInventoryDetailOfTarget.getInventory());
            mapper.update(materialInventoryDetailOfSource);
        }
        //4.保存移库历史记录
        WmsMaterialInventoryDetailRelocationLog materialInventoryDetailRelocationLog=new WmsMaterialInventoryDetailRelocationLog();
        materialInventoryDetailRelocationLog.setMaterialBatch(materialInventoryDetailOfSource.getMaterialBatch());//设置物料批次
        //设置源库位
        materialInventoryDetailRelocationLog.setWarehouseSeatOfSource(materialInventoryDetailOfSource.getWarehouseSeat());
        //设置目标库位
        materialInventoryDetailRelocationLog.setWarehouseSeatOfTarget(materialInventoryDetailOfTarget.getWarehouseSeat());//设置转移的目标库位。因为是入库，不用设置源库位
        //设置其他属性
        materialInventoryDetailRelocationLog.setSourceInventory(sourceInventory);//源库位的原库存
        materialInventoryDetailRelocationLog.setTargetInventory(targetInventory);//目标库位的原库存
        materialInventoryDetailRelocationLog.setRelocationQuantity(relocationQuantity);//转移的库存数量
        materialInventoryDetailRelocationLog.setReason(materialInventoryDetailOfTarget.getRemark());//移库原因
        //设置操作用户信息
        SysUser loginUser = UserUtil.getLoginUser();//获取当前登录用户
        materialInventoryDetailRelocationLog.setExecutor(loginUser);
        //存储移库日志
        materialInventoryDetailRelocationLogMapper.save(materialInventoryDetailRelocationLog);
    }

//    @Override
//    public void reserveForOutboundByMaterialBatch(WmsMaterialBatch materialBatch, Long number, String reason) {
//        //1.验证物料批次信息
//        if(materialBatch.getIsLock()){
//            throw new MyWebException("验证失败：该物料批次已经被锁定，无法使用");
//        }
//        if(number>materialBatch.getTotalInventory()-materialBatch.getLockQuantity()){
//            throw new MyWebException(String.format("验证失败：该物料批次库存数量不足%d个",materialBatch.getTotalInventory()-materialBatch.getLockQuantity()));
//        }
//        //2.获取该物料批次所有未锁定库位
//        WmsMaterialInventoryDetailQuery inventoryDetailQuery=new WmsMaterialInventoryDetailQuery();
//        inventoryDetailQuery.setMaterialBatch(materialBatch);
//        inventoryDetailQuery.setIsLock(false);
//        inventoryDetailQuery.setOrderType(5);//按库存升序排名
//        List<WmsMaterialInventoryDetail> inventoryDetails=mapper.findAll(inventoryDetailQuery);
//        //3.对库位执行出库
//        long remainingQuantity=number;//定义剩余数量
//        for(WmsMaterialInventoryDetail materialInventoryDetail:inventoryDetails){
//            long relocationQuantity=0;//成功预出库的数量
//            //3.1计算预出库的情况
//            if(remainingQuantity<=materialInventoryDetail.getInventory()-materialInventoryDetail.getLockQuantity()){
//                //如果出库数量小于该库位中的物料数量（要减去锁定数量），说明该库位1次就可以出完
//                relocationQuantity=remainingQuantity;
//                remainingQuantity=0;//一次出完
//                materialInventoryDetail.setLockQuantity(materialInventoryDetail.getLockQuantity()+relocationQuantity);//锁定数量增加
//            }else{//如果库位的物料数量不足
//                //预出库数量就是该库位中物料的剩余数量-锁定数量
//                relocationQuantity=materialInventoryDetail.getInventory()-materialInventoryDetail.getLockQuantity();
//                materialInventoryDetail.setLockQuantity(materialInventoryDetail.getLockQuantity()+relocationQuantity);//相当于将库存所有剩余物料全部锁定
//                remainingQuantity=number-relocationQuantity;//本次预出库后还差的数量
//            }
//            //3.2更新库位信息（主要是更新锁定数量）
//            mapper.update(materialInventoryDetail);
//            //如果已经全部出库就结束循环
//            if(remainingQuantity==0){
//                break;
//            }
//        }
//    }

    @Override
    public void reserveForOutboundByMaterialBatch(WmsMaterialBatch materialBatch, Long number, String reason) {
        //1.验证物料批次信息
        if(materialBatch.getIsLock()){
            throw new MyWebException("验证失败：该物料批次已经被锁定，无法使用");
        }
        if(number>materialBatch.getTotalInventory()-materialBatch.getLockQuantity()){
            throw new MyWebException(String.format("验证失败：该物料批次库存数量不足%d个",materialBatch.getTotalInventory()-materialBatch.getLockQuantity()));
        }
        //2.获取该物料批次所有未锁定库位
        WmsMaterialInventoryDetailQuery inventoryDetailQuery=new WmsMaterialInventoryDetailQuery();
        inventoryDetailQuery.setMaterialBatch(materialBatch);
        inventoryDetailQuery.setIsLock(false);
        inventoryDetailQuery.setOrderType(5);//按库存升序排名
        List<WmsMaterialInventoryDetail> inventoryDetails=mapper.findAll(inventoryDetailQuery);
        //3.对库位执行预出库
        long remainingQuantity=number;//定义剩余数量
        for(WmsMaterialInventoryDetail materialInventoryDetail:inventoryDetails){
            long relocationQuantity=0;//成功预出库的数量
            //3.1计算预出库的情况
            if(remainingQuantity<=materialInventoryDetail.getInventory()-materialInventoryDetail.getLockQuantity()){
                //如果出库数量小于该库位中的物料数量（要减去锁定数量），说明该库位1次就可以出完
                relocationQuantity=remainingQuantity;
                remainingQuantity=0;//一次出完
                materialInventoryDetail.setLockQuantity(materialInventoryDetail.getLockQuantity()+relocationQuantity);//锁定数量增加
            }else{//如果库位的物料数量不足
                //预出库数量就是该库位中物料的剩余数量-锁定数量
                relocationQuantity=materialInventoryDetail.getInventory()-materialInventoryDetail.getLockQuantity();
                materialInventoryDetail.setLockQuantity(materialInventoryDetail.getLockQuantity()+relocationQuantity);//相当于将库存所有剩余物料全部锁定
                remainingQuantity=number-relocationQuantity;//本次预出库后还差的数量
            }
            //3.2更新库位信息（主要是更新锁定数量）
            mapper.update(materialInventoryDetail);
            //4.记录预出库日志（锁定数量）-订单暂时不录入
            WmsMaterialInventoryDetailLockLog materialInventoryDetailLockLog=new WmsMaterialInventoryDetailLockLog();
            String productionOrderId=this.extractOrderNumber(reason);//提取生产订单号
            if(productionOrderId!=null){
                materialInventoryDetailLockLog.setProductionOrder(new MesProductionOrder(productionOrderId));
            }
            materialInventoryDetailLockLog.setId(IdUtil.fastSimpleUUID());
            materialInventoryDetailLockLog.setMaterialBatch(materialBatch);
            materialInventoryDetailLockLog.setWarehouseSeat(materialInventoryDetail.getWarehouseSeat());
            materialInventoryDetailLockLog.setSeat(materialInventoryDetail.getWarehouseSeat().getName());
            materialInventoryDetailLockLog.setArea(materialInventoryDetail.getWarehouseSeat().getWarehouseArea().getName());
            materialInventoryDetailLockLog.setWarehouse(materialInventoryDetail.getWarehouseSeat().getWarehouseArea().getWarehouse().getName());
            materialInventoryDetailLockLog.setLockType("lock");//锁定状态
            materialInventoryDetailLockLog.setPreLockedQuantity(materialInventoryDetail.getLockQuantity()-relocationQuantity);//原锁定数量
            materialInventoryDetailLockLog.setDeltaQuantity(relocationQuantity);//本次锁定的数量
            materialInventoryDetailLockLog.setInventory(materialInventoryDetail.getInventory());//原库存
            materialInventoryDetailLockLog.setExecutor(UserUtil.getLoginUser());//设置操作用户
            materialInventoryDetailLockLog.setReason(reason);//锁定原因
            wmsMaterialInventoryDetailLockLogMapper.save(materialInventoryDetailLockLog);
            //如果已经全部出库就结束循环
            if(remainingQuantity==0){
                break;
            }
        }
    }

    @Override
    public void settleReservationByMaterialBatch(WmsMaterialBatch materialBatch, String reservationAction,Long number, String reason) {
        // 1. 验证动作类型
        String actionName=ReservationAction.getActionNameByAction(reservationAction);
        if (actionName==null) {
            throw new MyWebException("验证失败：出库动作传参不正确，只能使用:" +ReservationAction.getValidActions());
        }
        //2.验证物料批次信息
        if(materialBatch.getIsLock()){
            throw new MyWebException("出库失败：该物料批次已经被锁定，无法使用");
        }
        if (number > materialBatch.getLockQuantity()) {
            throw new MyWebException(String.format("出库失败：该物料批次锁定数量只有%d，不足%d，系统锁定数量计算代码有误，请联系系统管理员检查！"
                    , materialBatch.getLockQuantity()
                    , number));
        }
        //3.获取该物料批次所有未锁定库位
        WmsMaterialInventoryDetailQuery inventoryDetailQuery=new WmsMaterialInventoryDetailQuery();
        inventoryDetailQuery.setMaterialBatch(materialBatch);
        inventoryDetailQuery.setIsLock(false);
        if(reservationAction.equals(ReservationAction.CONFIRM_OUTBOUND.name())) {//如果是出库
            inventoryDetailQuery.setOrderType(5);//按有效库存升序排名
        }else{//如果是撤销出库
            inventoryDetailQuery.setOrderType(6);//按有效库存降序排名
        }
        List<WmsMaterialInventoryDetail> inventoryDetails=mapper.findAll(inventoryDetailQuery);
        //4.对库位执行出库
        long remainingQuantity=number;//定义剩余数量
        for(WmsMaterialInventoryDetail materialInventoryDetail:inventoryDetails){
            long inventoryOfOrignl=materialInventoryDetail.getInventory();//获取库存原数量，用于后续日志
            long relocationQuantity=0;//成功出库的数量
            //4.1计算出库的情况
            if(materialInventoryDetail.getLockQuantity()==0){//如果锁定数量为0，则直接停止本次循环
                continue;
            }
            if(remainingQuantity<=materialInventoryDetail.getLockQuantity()){
                //如果出库数量小于该库位中的锁定数量，说明该库位1次就可以出完
                relocationQuantity=remainingQuantity;
                remainingQuantity=0;//一次出完
                materialInventoryDetail.setLockQuantity(materialInventoryDetail.getLockQuantity()-relocationQuantity);//锁定数量减少
            }else{//如果库位的锁定数量不足
                //出库数量就是该库位中物料锁定数量
                relocationQuantity=materialInventoryDetail.getLockQuantity();
                materialInventoryDetail.setLockQuantity(0l);//相当于将库存所有锁定物料清零
                remainingQuantity=number-relocationQuantity;//本次预出库后还差的数量
            }
            //4.2更新库位信息，如果是出库且符合条件则移除库位信息。并记录日志
            if(reservationAction.equals(ReservationAction.CONFIRM_OUTBOUND.name())) {//如果是出库
                //4.2.1 在系统中更新库位信息
                materialInventoryDetail.setInventory(materialInventoryDetail.getInventory()-relocationQuantity);//库存数量减少
                if(materialInventoryDetail.getInventory()==0&&materialInventoryDetail.getLockQuantity()==0){
                    //如果库位剩余库存为0并且锁定数量为0,移除该库位
                    mapper.deleteById(materialInventoryDetail.getId());
                }else{//更新库存中的库存和锁定数量
                    mapper.update(materialInventoryDetail);
                }
                //4.2.2 记录出库日志
                WmsMaterialInventoryDetailRelocationLog materialInventoryDetailRelocationLog=new WmsMaterialInventoryDetailRelocationLog();
                materialInventoryDetailRelocationLog.setMaterialBatch(materialBatch);//设置物料批次
                materialInventoryDetailRelocationLog.setWarehouseSeatOfSource(materialInventoryDetail.getWarehouseSeat());//出库的库位
                materialInventoryDetailRelocationLog.setSourceInventory(inventoryOfOrignl);//原库存
                materialInventoryDetailRelocationLog.setRelocationQuantity(relocationQuantity);//出库的数量
                materialInventoryDetailRelocationLog.setReason(reason);//出库原因
                //设置操作用户信息
                SysUser loginUser = UserUtil.getLoginUser();//获取当前登录用户
                materialInventoryDetailRelocationLog.setExecutor(loginUser);
                //存储移库日志
                materialInventoryDetailRelocationLogMapper.save(materialInventoryDetailRelocationLog);
            }else{//如果是撤销车库
                //更新库存中的锁定数量
                mapper.update(materialInventoryDetail);
            }
            //5.记录解锁物料批次数量的记录-订单暂时不录入
            WmsMaterialInventoryDetailLockLog materialInventoryDetailLockLog=new WmsMaterialInventoryDetailLockLog();
            materialInventoryDetailLockLog.setId(IdUtil.fastSimpleUUID());
            String productionOrderId=this.extractOrderNumber(reason);//提取生产订单号
            if(productionOrderId!=null){
                materialInventoryDetailLockLog.setProductionOrder(new MesProductionOrder(productionOrderId));
            }
            materialInventoryDetailLockLog.setMaterialBatch(materialBatch);
            materialInventoryDetailLockLog.setWarehouseSeat(materialInventoryDetail.getWarehouseSeat());
            materialInventoryDetailLockLog.setSeat(materialInventoryDetail.getWarehouseSeat().getName());
            materialInventoryDetailLockLog.setArea(materialInventoryDetail.getWarehouseSeat().getWarehouseArea().getName());
            materialInventoryDetailLockLog.setWarehouse(materialInventoryDetail.getWarehouseSeat().getWarehouseArea().getWarehouse().getName());
            materialInventoryDetailLockLog.setLockType("unlock");//解锁状态
            materialInventoryDetailLockLog.setPreLockedQuantity(materialInventoryDetail.getLockQuantity()+relocationQuantity);//原锁定数量
            materialInventoryDetailLockLog.setDeltaQuantity(relocationQuantity);//本次解锁的数量
            materialInventoryDetailLockLog.setInventory(materialInventoryDetail.getInventory());//原库存
            materialInventoryDetailLockLog.setExecutor(UserUtil.getLoginUser());//设置操作用户
            materialInventoryDetailLockLog.setReason(reason);//锁定原因
            wmsMaterialInventoryDetailLockLogMapper.save(materialInventoryDetailLockLog);
            //如果已经全部出库就结束循环
            if(remainingQuantity==0){
                break;
            }
        }
    }

//    @Override
//    public void lockByMaterialBatch(WmsMaterialBatch materialBatch, Long number, String reason) {
//
//    }

    @Override
    public void outboundByMaterialBatch(WmsMaterialBatch materialBatch, Long number,String reason) {
        //1.验证物料批次信息
        if(materialBatch.getIsLock()){
            throw new MyWebException("验证失败：该物料批次已经被锁定，无法使用");
        }
        if(number>materialBatch.getTotalInventory()-materialBatch.getLockQuantity()){
            throw new MyWebException(String.format("验证失败：该物料批次库存数量不足%d个",materialBatch.getTotalInventory()-materialBatch.getLockQuantity()));
        }
        //2.获取该物料批次所有未锁定库位
        WmsMaterialInventoryDetailQuery inventoryDetailQuery=new WmsMaterialInventoryDetailQuery();
        inventoryDetailQuery.setMaterialBatch(materialBatch);
        inventoryDetailQuery.setIsLock(false);
        inventoryDetailQuery.setOrderType(5);//按库存升序排名
        List<WmsMaterialInventoryDetail> inventoryDetails=mapper.findAll(inventoryDetailQuery);
        //3.对库位执行出库
        long remainingQuantity=number;//定义剩余数量
        for(WmsMaterialInventoryDetail materialInventoryDetail:inventoryDetails){
            long relocationQuantity=0;//成功出库的数量
            //3.1计算出库的情况
            if(remainingQuantity<=materialInventoryDetail.getInventory()-materialInventoryDetail.getLockQuantity()){
                //如果出库数量小于该库位中的物料数量（要减去锁定数量），说明该库位1次就可以出完
                relocationQuantity=remainingQuantity;
                materialInventoryDetail.setInventory(materialInventoryDetail.getInventory()-remainingQuantity);//剩余库存
            }else{//如果库位的物料数量不足
                //出库数量就是该库位中物料的所有数量（不包括锁定数量）
                relocationQuantity=materialInventoryDetail.getInventory();
                remainingQuantity=number-relocationQuantity;//本次出库后还差的数量
            }
            //3.2更新库位信息
            if(materialInventoryDetail.getInventory()==0&&materialInventoryDetail.getLockQuantity()==0){
                //如果库位剩余库存为0并且锁定数量为0,移除该库位
                mapper.deleteById(materialInventoryDetail.getId());
            }else{
                //更新库存信息
                mapper.update(materialInventoryDetail);
            }
            //3.3 记录出库日志
            WmsMaterialInventoryDetailRelocationLog materialInventoryDetailRelocationLog=new WmsMaterialInventoryDetailRelocationLog();
            materialInventoryDetailRelocationLog.setMaterialBatch(materialBatch);//设置物料批次
            materialInventoryDetailRelocationLog.setWarehouseSeatOfSource(materialInventoryDetail.getWarehouseSeat());//出库的库位
            materialInventoryDetailRelocationLog.setRelocationQuantity(remainingQuantity);//出库的数量
            materialInventoryDetailRelocationLog.setReason(reason);//出库原因
            //设置操作用户信息
            SysUser loginUser = UserUtil.getLoginUser();//获取当前登录用户
            materialInventoryDetailRelocationLog.setExecutor(loginUser);
            //存储移库日志
            materialInventoryDetailRelocationLogMapper.save(materialInventoryDetailRelocationLog);
            //如果已经全部出库就结束循环
            if(remainingQuantity==0){
                break;
            }
        }
    }

    @Override
    public void inboundByMaterialBatch(WmsMaterialBatch materialBatch, Long number, String reason) {
//        //1.验证物料批次信息
//        if(materialBatch.getIsLock()){
//            throw new MyWebException("入库失败：该物料批次已经被锁定，暂时无法入库");
//        }
//        //2.获取物料的库存信息，不超过物料库存上限
//        WmsMaterialInventory materialInventory = materialInventoryMapper.findById(materialBatch.getMaterialId());
//        if(materialInventory!=null){
//            if(materialInventory.getInventoryLimit()<number+materialInventory.getTotalInventory()+materialInventory.getLockQuantity()){
//                throw new MyWebException(String.format("入库失败失败：该物料库存上限为%d，目前库存%d、锁定数量%d，再存入%d的物料将超出上限",
//                        materialInventory.getInventoryLimit(),
//                        materialInventory.getTotalInventory(),
//                        materialInventory.getLockQuantity(),
//                        number));
//            }
//        }else{
//            throw new MyWebException("入库失败：该物料还没有建立库存，请先建立再入库");
//        }
//        //3.获取该物料批次所有未锁定库位
//        WmsMaterialInventoryDetailQuery inventoryDetailQuery=new WmsMaterialInventoryDetailQuery();
//        inventoryDetailQuery.setMaterialBatch(materialBatch);
//        inventoryDetailQuery.setIsLock(false);
//        inventoryDetailQuery.setOrderType(6);//按有效库存降序排名
//        List<WmsMaterialInventoryDetail> inventoryDetails=mapper.findAll(inventoryDetailQuery);
//        //4.对库位执行入库
//        if(inventoryDetails.size()>0){//如果已经有库位就存储在库位上
//            for(WmsMaterialInventoryDetail materialInventoryDetail:inventoryDetails) {
//                //4.1先判断库位是否满库,如果满库则换下一个库位
//                if (materialInventoryDetail.getWarehouseSeat().getIsFull()) {
//                    continue;
//                }else{//如果没有满库
//                    materialInventoryDetail.setInventory(materialInventoryDetail.getInventory()+number);//更新现有库存
//                }
//            }
//        }
//        for(WmsMaterialInventoryDetail materialInventoryDetail:inventoryDetails){
//            //3.2更新库位信息
//            if(materialInventoryDetail.getInventory()==0&&materialInventoryDetail.getLockQuantity()==0){
//                //如果库位剩余库存为0并且锁定数量为0,移除该库位
//                mapper.deleteById(materialInventoryDetail.getId());
//            }else{
//                //更新库存信息
//                mapper.update(materialInventoryDetail);
//            }
//            //3.3 记录出库日志
//            WmsMaterialInventoryDetailRelocationLog materialInventoryDetailRelocationLog=new WmsMaterialInventoryDetailRelocationLog();
//            materialInventoryDetailRelocationLog.setMaterialBatch(materialBatch);//设置物料批次
//            materialInventoryDetailRelocationLog.setWarehouseSeatOfSource(materialInventoryDetail.getWarehouseSeat());//出库的库位
//            materialInventoryDetailRelocationLog.setRelocationQuantity(remainingQuantity);//出库的数量
//            materialInventoryDetailRelocationLog.setReason(reason);//出库原因
//            //设置操作用户信息
//            SysUser loginUser = UserUtil.getLoginUser();//获取当前登录用户
//            materialInventoryDetailRelocationLog.setExecutor(loginUser);
//            //存储移库日志
//            materialInventoryDetailRelocationLogMapper.save(materialInventoryDetailRelocationLog);
//            //如果已经全部出库就结束循环
//            if(remainingQuantity==0){
//                break;
//            }
//        }
    }

    /**
     * 从文本中提取订单号,约定好的格式。
     * 说明：
     * 这个是为了取巧，直接从日志中获取订单号，避免直接传递订单号带来的模块耦合，但是要保证其他模块调用时传递过来的文本格式不变。
     * 将物料永磁同步电机30kW用于生产订单(9a0f7664a12c4fc281683505eea6dac8)、车辆(VIN:029f0bf2afc8447cb094b6871878a83f)第2步的生产
     * @param text 输入文本（如"将物料用于生产订单(abc123、车辆(VIN:xyz)第2步的生产"）
     * @return 订单号（若存在则返回，否则返回null）
     */
    private String extractOrderNumber(String text) {
        String orderNumber=null;//订单号
        /// 定义结构化正则表达式
        String regex = "生产订单\\((.*?)\\)、";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(text);
        // 查找第一个匹配项
        if (matcher.find()) {
            // 返回括号内的内容（trim()去除前后空白）
            orderNumber=matcher.group(1).trim();
        }
        return orderNumber;
    }
}
